#include "log_bill.h"

log_bill *log_bill::m_instance = NULL;

log_bill::log_bill()
{
	m_logsize = 0;
	m_logfile.fd = -1;
	m_logfile.seq = 0;
	memset(m_logname, 0, 16);
	memset(m_logdir, 0, 128);
	pthread_mutex_init(&m_mutex, NULL);
}

log_bill::~log_bill()
{
	if (m_logfile.fd > 0)
		m_logfile.fd = -1;

	pthread_mutex_unlock(&m_mutex);
}

log_bill *log_bill::instance()
{
	if (m_instance == NULL)
		m_instance = new log_bill();

	return m_instance;
}

int log_bill::init(loglevel_t loglevel, const char *logdir, const char *logname, int logsize)
{
	if (loglevel < LM_ALL || loglevel > LM_NONE)
	{
		fprintf(stderr, "the loglevel error, loglevel: %d\n", loglevel);
		return -1;
	}
	m_loglevel = loglevel;

	if (logsize > 0)
		m_logsize = logsize;
	else
		m_logsize = LOG_DEFAULT_SIZE;

	if (logdir == NULL || strlen(logdir) == 0)
	{
		strncpy(m_logdir, "/tmp", strlen("/tmp"));
		m_logdir[strlen("/tmp")] = '\0';
	}
	else
	{
		strncpy(m_logdir, logdir, strlen(logdir));
		m_logdir[strlen(logdir)] = '\0';

		if (access(m_logdir, R_OK | W_OK | F_OK) == -1)
		{
			if (-1 == createdir(logdir))
			{
				fprintf(stderr, "create dir error. directory is: %s\n", m_logdir);

				return -1;
			}
		}
	}

	if (logname == NULL || strlen(logname) == 0)
	{
		strncpy(m_logname, "error.log", strlen("error.log"));
		m_logname[strlen("error.log")] = '\0';
	}
	else
	{
		strncpy(m_logname, logname, strlen(logname));
		m_logname[strlen(logname)] = '\0';
	}

	return 0;
}

int log_bill::writelog(loglevel_t loglevel, const char *fmt, ...)
{
	va_list ap;
	va_start(ap, fmt);
	int ret = vwritelog(loglevel, fmt, ap);
	va_end(ap);

	return ret;
}

int log_bill::vwritelog(loglevel_t loglevel, const char *fmt, va_list ap)
{
	if (loglevel > m_loglevel)
		return 0;

    time_t now = time(NULL);
    struct tm stm;
    localtime_r(&now, &stm);

    int fp = -1;
    pthread_mutex_lock(&m_mutex);
    
    if ((fp = openfile(stm)) == -1)
    {
        vfprintf(stderr, fmt, ap);
        pthread_mutex_unlock(&m_mutex);

        return 0;
    }

    int prelen = 0;
    int infolen = 0;

    char logbuf[1024];
    memset(logbuf, 0, 1024);
    
    prelen = snprintf(logbuf, 1024, "[%04d/%02d/%02d %02d:%02d:%02d]: ", stm.tm_year + 1900, stm.tm_mon + 1, stm.tm_mday, \
                     stm.tm_hour, stm.tm_min, stm.tm_sec);
    infolen = vsnprintf(logbuf + prelen, 1024 - prelen, fmt, ap);

    write(fp, logbuf, prelen + infolen);
    write(fp, "\n", 1);

    shiftlogfiles(stm);
    pthread_mutex_unlock(&m_mutex);
    
	return 0;
}

int log_bill::shiftlogfiles(struct tm &stm)
{
    if (m_logfile.fd < 0)
        return -1;

    long length = lseek(m_logfile.fd, 0, SEEK_END);
    if (length < 0)
        return -1;

    if (length < LOG_DEFAULT_SIZE && m_logfile.day == stm.tm_yday)
        return 0;
    
    close(m_logfile.fd);
    m_logfile.fd = -1;
    m_logfile.seq++;
    
    return 0;
}

int log_bill::openfile(struct tm &stm)
{
    if (m_logfile.fd > 0)
        return m_logfile.fd;

    char logfile[128];
    memset(logfile, 0, 128);
    getfilename(logfile, 128);

    int fp = open(logfile, O_WRONLY | O_CREAT | O_APPEND, 0644);
    if (fp < 0)
    {
        fprintf(stderr, "open log file error.file is: %s\n", m_logname);
        return -1;
    }

    int val = fcntl(fp, F_GETFD, 0);
    val |= FD_CLOEXEC;
    fcntl(fp, F_SETFD, val);

    m_logfile.fd = fp;
	m_logfile.day = stm.tm_yday;
    
    return m_logfile.fd;
}

int log_bill::getfilename(char *logfile, int size)
{
    for (int seq = m_logfile.seq; seq < LOG_FILE_NUM; seq++)
    {
        snprintf(logfile, size, "%s/%s.%d", m_logdir, m_logname, seq);

        if (access(logfile, F_OK))
        {
            m_logfile.seq = seq;
            snprintf(logfile, 128, "%s/%s.%d", m_logdir, m_logname, m_logfile.seq);

            return 0;
        }
    }

    snprintf(logfile, size, "%s/%s", "/tmp", m_logname);
    
    return 0;
}

int createdir(const char *path)
{
	char currdir[128];
	char dirbuf[128];
	memset(currdir, 0, 128);

	printf("path is: %s\n", path);
	char *pcurr = NULL;
	char *pnext = NULL;

	strncpy(currdir, path, strlen(path));
	currdir[strlen(path)] = '\0';

	if (currdir[0] == '/')
		pcurr = &currdir[1];
	else
		pcurr = &currdir[0];

	while ((pnext = strchr(pcurr, '/')) != NULL)
	{
		memset(dirbuf, 0, 128);
		strncpy(dirbuf, currdir, pnext-currdir);
		dirbuf[pnext-currdir] = '\0';

		if (mkdir(dirbuf, 0700) == -1)
		{
			if (errno != EEXIST)
			{
				return -1;
			}
		}

		pcurr = pnext + 1;
	}

	if (mkdir(currdir, 0700) == -1)
	{
		if (errno != EEXIST)
		{
			return -1;
		}
	}

	return 0;
}

int APIlogwrite(loglevel_t loglevel, const char *fmt, ...)
{
	va_list ap;
	va_start(ap, fmt);
	int ret = log_bill::instance()->vwritelog(loglevel, fmt, ap);
	va_end(ap);

	return ret;
}
